package net.cyue.nest.nestjava.core.injector;

import net.cyue.nest.nestjava.common.annotation.core.Inject;
import net.cyue.nest.nestjava.common.data.MetaType;
import net.cyue.nest.nestjava.common.util.ReflectUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class DefaultInstanceRegistry extends InstanceRegistry {

  public DefaultInstanceRegistry() {
    this.instanceWrapperMap = new HashMap<>();
  }


  /**
   * 根据给定的 MetaType 创建对应的实例，并封装到 InstanceWrapper 中。
   *
   * @param metaType 包含待创建实例信息的 MetaType 对象，其中包含了类的类型信息。
   * @return 创建好的 InstanceWrapper 对象，其中包含了实例化的对象。
   */
  private InstanceWrapper createInstance(MetaType metaType, ModuleData host) {
    // metaType 可能为 InjectableMetaType
    MetaType mt = new MetaType(metaType.getClz());
    // 实例已存在，直接返回
    if (instanceWrapperMap.containsKey(mt)) {
      return this.getInstanceWrapper(mt);
    }

    // 获取类的所有构造函数
    Class<?> clz = metaType.getClz();
    Constructor<?>[] constructorArray = clz.getDeclaredConstructors();
    // 如果类没有构造函数，则抛出异常
    if (constructorArray.length == 0) {
      throw new RuntimeException("instance 必须有构造函数");
    };

    // 取第一个构造函数，并设置其可访问
    Constructor<?> constructor = constructorArray[0];
    constructor.setAccessible(true);
    // 获取构造函数的参数类型
    Parameter[] parameters = constructor.getParameters();
    Class<?>[] parameterTypes = constructor.getParameterTypes();

    // 为构造函数的每个参数准备实例
    ArrayList<Object> constructParams = new ArrayList<>();
    for (int i = 0; i < parameters.length; i++) {
      Parameter p = parameters[i];
      Class<?> pClz = parameterTypes[i];
      Inject inject = p.getAnnotation(Inject.class);
      if (inject == null) {
        // 拿不到 injectable 对象实例
        Object instance = this.getInstance(pClz);
        if (instance != null) {
          constructParams.add(instance);
        }
        continue;
      }

      Object instance = Injector.getInjectableInstance(inject);
      if (instance != null) {
        constructParams.add(instance);
      }
    }

    // 创建对象实例并初始化对象字段
    Object instance = null;
    try {
      // 使用准备好的参数实例化对象
      instance = constructor.newInstance(constructParams.toArray());

      // 给对象的空字段赋值
      Field[] fields = clz.getDeclaredFields();
      for (Field field : fields) {
        field.setAccessible(true);
        Object fieldInstance = field.get(instance);
        // 字段非空，跳过
        if (fieldInstance != null) {
          continue;
        }

        // Inject 注解相关
        Inject injectAnnotation = field.getAnnotation(Inject.class);
        MetaType fMT = new MetaType(field.getType());
        if (injectAnnotation == null) {
          fieldInstance = this.getInstance(fMT);
          if (fieldInstance != null) {
            ReflectUtil.setFieldValue(instance, field, fieldInstance);
          }
          continue;
        }

        fieldInstance = Injector.getInjectableInstance(injectAnnotation);
        if (fieldInstance != null) {
          ReflectUtil.setFieldValue(instance, field, fieldInstance);
        }
      }
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
      throw new RuntimeException(e);
    }

    // 创建并返回 InstanceWrapper 实例
    return new InstanceWrapper(
      metaType,
      metaType.getName(),
      host,
      instance
    );
  }


  /**
   * 注册一个实例到实例映射表中。
   * 此方法首先会递归检查并注册该实例的所有依赖项，然后为该实例创建一个实例包装器，并将其添加到映射表中。
   *
   * @param metaType 要注册的实例的元类型，包含实例的类信息和依赖信息。
   */
  @Override
  public InstanceWrapper registerInstance(MetaType metaType, ModuleData host) {
    List<MetaType> dependencies = metaType.getDependencies();
    for(MetaType dependency : dependencies) {
      // 如果依赖项已经存在于映射表中，则跳过此次循环
      if (this.instanceWrapperMap.containsKey(dependency)) {
        continue;
      }

      // 递归注册依赖项
      this.registerInstance(dependency, host);
      // 为依赖项创建实例包装器
      InstanceWrapper wrapper = this.createInstance(dependency, host);
      // 将依赖项的实例包装器添加到映射表中
      this.instanceWrapperMap.put(
        dependency,
        wrapper
      );
    }

    // 为当前实例创建实例包装器
    InstanceWrapper wrapper = this.createInstance(metaType, host);
    // 将当前实例的实例包装器添加到映射表中
    this.instanceWrapperMap.put(
      metaType,
      wrapper
    );
    return wrapper;
  }

}

